package com.pd.secure;


import java.io.*;
import java.net.URL;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Enumeration;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author: dong peng
 * @date: 2018/7/5 16:40
 * @description:
 */

public class RsaSingnature {

    private static final String SIGNATURE_ALGORITHM_SHA1_WITH_RSA = "SHA1withRSA";

    private String encode;

    private final ConcurrentHashMap<String,PrivateKey> priKeyMap = new ConcurrentHashMap<>();

    private final ConcurrentHashMap<String,PublicKey> pubKeyMap = new ConcurrentHashMap<>();

    private static RsaSingnature rsaSingnature = null;

    private RsaSingnature(){

    }

    public static RsaSingnature getInstance(String encode){
        if(rsaSingnature == null) {
            synchronized (RsaSingnature.class) {
                if(rsaSingnature == null) {
                    rsaSingnature = new RsaSingnature(encode);
                }
            }
        }

        return rsaSingnature;

    }

    public RsaSingnature(String encode) {
        this.encode = encode;
    }

    public byte[] sign(String pfxFileName, String pfxPassword, String plainData) throws Exception {
        if(pfxFileName == null || "".equals(pfxFileName.trim())) {
            throw new RuntimeException("私钥文件路径不能是空");
        }
        Signature signature = Signature.getInstance(this.SIGNATURE_ALGORITHM_SHA1_WITH_RSA);
        PrivateKey privateKey = priKeyMap.get(pfxFileName);
        if(privateKey == null) {
            synchronized (this) {
                privateKey = this.readPriKey(pfxFileName,pfxPassword);
                priKeyMap.put(pfxFileName,privateKey);
            }
        }
        signature.initSign(privateKey);
        signature.update(plainData.getBytes(encode));
        return signature.sign();
    }

    public boolean verify(String certPath, byte[] plainData, byte[] signData) throws Exception {
        if(certPath == null || "".equals(certPath.trim())) {
            throw new RuntimeException("公钥文件路径不能是空");
        }
        Signature sig = Signature.getInstance(this.SIGNATURE_ALGORITHM_SHA1_WITH_RSA);
        sig.initVerify(this.readPublicKey(certPath));
        sig.update(plainData);
        return sig.verify(signData);
    }

    public boolean verify(String certPath, String plainData, byte[] signData) throws Exception {
        if(certPath == null || "".equals(certPath.trim())) {
            throw new RuntimeException("公钥文件路径不能是空");
        }
        Signature sig = Signature.getInstance(this.SIGNATURE_ALGORITHM_SHA1_WITH_RSA);
        PublicKey publicKey = pubKeyMap.get(certPath);
        if(publicKey == null) {
            synchronized (this) {
                publicKey = this.readPublicKey(certPath);
                pubKeyMap.put(certPath,publicKey);
            }
        }
        sig.initVerify(publicKey);
        sig.update(plainData.getBytes(encode));
        return sig.verify(signData);
    }

    private PrivateKey readPriKey(String pfxFileName, String pfxPassword) throws IOException
            , KeyStoreException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException, InvalidKeySpecException {
        InputStream fileInputstream = null;
        File file = new File(pfxFileName);
        if (file.exists()) {
            fileInputstream = new FileInputStream(pfxFileName);
        } else {
            Class<? extends Signature> clz = Signature.class;
            ClassLoader cl = clz.getClassLoader();

            URL url = cl.getResource(pfxFileName);
            if (url == null) {
                throw new FileNotFoundException();
            }

            fileInputstream = cl.getResourceAsStream(pfxFileName);
        }

        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        keyStore.load(fileInputstream, pfxPassword == null ? null : pfxPassword.toCharArray());
        String alias = null;
        Enumeration<String> aliases = keyStore.aliases();
        if (aliases.hasMoreElements()) {
            alias = aliases.nextElement().toString();
        }

        Key key = keyStore.getKey(alias, pfxPassword == null ? null : pfxPassword.toCharArray());
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(key.getEncoded());
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        if(fileInputstream != null){
            fileInputstream.close();
        }
        return keyFactory.generatePrivate(keySpec);
    }


    private PublicKey readPublicKey(String certPath)
            throws CertificateException, IOException {

        File file = new File(certPath);
        InputStream is = null;
        if (file.exists()) {
            is = new FileInputStream(certPath);
        } else {
            Class<? extends Signature> clz = Signature.class;
            ClassLoader cl = clz.getClassLoader();

            URL url = cl.getResource(certPath);
            if (url == null) {
                throw new FileNotFoundException();
            }

            is = cl.getResourceAsStream(certPath);
        }
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
        if (is != null) {
            is.close();
        }
        return cert.getPublicKey();
    }


//    public static void main(String[] args) throws InterruptedException {
//        long start = System.currentTimeMillis();
//
//        CountDownLatch latch = new CountDownLatch(100);
//
//        for (int i = 0; i < 100; i++) {
//            new Thread(new Runnable() {
//                @Override
//                public void run() {
//                String priPath = "E:\\channel\\cibyypay\\merchant001-rsa.pfx";
//                String pubPath = "E:\\channel\\cibyypay\\pubServer-rsa.cer";
//                try {
//                    byte[] s = RsaSingnature.getInstance("UTF-8").sign(priPath, "222222", "12321321223");
//
//                    System.out.println(new String(s, "UTF-8"));
//                } catch (Exception e) {
//                    e.printStackTrace();
//                }
//
//                    latch.countDown();
//                }
//            }).start();
//
//
//        }
//        latch.await();
//        System.out.println("耗时: =========>"+(System.currentTimeMillis()-start));
//    }
}
